
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Serpis
{
	public struct PropertyError
	{
		public PropertyError(string property, string error)
		{
			Property = property;
			Error = error;
		}
		public readonly string Property;
		public readonly string Error;
	}

	public class Validator
	{
		private struct PropertyValidator
		{
			public PropertyValidator(PropertyInfo propertyInfo, ValidatorBaseAttribute validatorBase)
			{
				PropertyInfo = propertyInfo;
				ValidatorBase = validatorBase;
			}
			public PropertyInfo PropertyInfo;
			public ValidatorBaseAttribute ValidatorBase;
			
		}
		private object model;
		private List<PropertyError> errors = new List<PropertyError>();
		
		private List<PropertyValidator> propertyValidators = new List<PropertyValidator>();
		
		private object[] typeValidators;
		
		public Validator (object model)
		{
			this.model = model;
			
			Type modelType = model.GetType();
			
			typeValidators = modelType.GetCustomAttributes(typeof(ValidatorBaseAttribute), true);
			
			foreach (PropertyInfo propertyInfo in modelType.GetProperties()) 
				if (propertyInfo.IsDefined(typeof(ValidatorBaseAttribute), true)) {
					object[] validators = propertyInfo.GetCustomAttributes(typeof(ValidatorBaseAttribute), true);
					foreach (ValidatorBaseAttribute validatorBase in validators)
						propertyValidators.Add(new PropertyValidator(propertyInfo, validatorBase));
				}
			
		}
		
		public void Validate()
		{
			errors.Clear();
			
			foreach (ValidatorBaseAttribute validatorBase in typeValidators) {
				string error = validatorBase.Validate(model);
				if (error != "")
					errors.Add(new PropertyError("", error));
			}
			
			foreach (PropertyValidator propertyValidator in propertyValidators) {
				object value = propertyValidator.PropertyInfo.GetValue(model, null);
				string error = propertyValidator.ValidatorBase.Validate(value);
				if (error != "")
					errors.Add(new PropertyError(propertyValidator.PropertyInfo.Name, error));
			}
		}
		
		public bool IsValid {
			get {return errors.Count == 0;}
		}
		
		public PropertyError[] Errors {
			get {return errors.ToArray();}
		}
	}
}
